import type { FacilityStatus, MetroDetail } from "@dci/core"; import { ExternalLink } from "lucide-react"; import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { Breadcrumbs } from "@/components/entity/breadcrumbs"; import { EntityMap } from "@/components/entity/entity-map"; import { JsonLd } from "@/components/entity/json-ld"; import { fPageHref, PagedFacilities, parseFPage } from "@/components/entity/paged-facilities"; import { SectionNav } from "@/components/entity/section-nav"; import { SourcesFooter } from "@/components/entity/sources-footer"; import { EventList } from "@/components/event-list"; import { ClaimsTable } from "@/components/evidence"; import { FacilityTable } from "@/components/facility-table"; import { MarketConcentrationPanel } from "@/components/intelligence/market/concentration"; import { MarketMomentumPanel } from "@/components/intelligence/market/momentum"; import { OpeningTimeline } from "@/components/intelligence/market/opening-timeline"; import { ApiNote, CoverageBar, CoverageStrip, GridConstraintList, HonestEmpty, PillLink, PipelineBars, PublishedOnlyNote, ShareList } from "@/components/intelligence/shared"; import { WatchButton } from "@/components/intelligence/watch-button"; import { ProjectList } from "@/components/project-list"; import { Badge, EstimateMark } from "@/components/ui/badge"; import { Page, Section } from "@/components/ui/card"; import { StackedBar } from "@/components/ui/charts"; import { CountryFlag } from "@/components/ui/country-flag"; import { Metric, MetricCell, MetricRow } from "@/components/ui/metric"; import { ErrorState, OfflineNotice } from "@/components/ui/states"; import { RowLink, TBody, THead, Table, Td, Th, Tr } from "@/components/ui/table"; import type { DetailFacilitiesMeta } from "@/lib/dto"; import { fmtDate, fmtInt, fmtMw, fmtPct, pluralize, splitMw } from "@/lib/format"; import { routes } from "@/lib/routes"; import { compactJsonLd, NOT_FOUND_METADATA, pageMetadata } from "@/lib/seo"; import type { SearchParams } from "@/lib/sort"; import { countryHref, countrySlugMap, pipelineMw } from "../country-links"; import { getMetro } from "./data"; import { GrowthChart } from "./growth-chart"; export const revalidate = 300; type Props = { params: Promise<{ slug: string }>; searchParams: Promise }; function describe(m: MetroDetail): string { const parts = [`${fmtInt(m.facilityCount)} data center ${m.facilityCount === 1 ? "facility" : "facilities"} in ${m.name}${m.countryName ? `, ${m.countryName}` : ""}`]; if (m.knownMw !== null) parts.push(`${fmtMw(m.knownMw)} known operational capacity (${fmtPct(m.mwCoverage)} of facilities publish a figure)`); const pipe = pipelineMw(m); if (pipe !== null) parts.push(`${fmtMw(pipe)} in the pipeline`); parts.push(`${fmtInt(m.operatorCount)} operators`); if (m.cloudRegionCount) parts.push(`${fmtInt(m.cloudRegionCount)} cloud regions`); return `${parts.join(", ")}. Operators, concentration, momentum, pipeline, connectivity, power context and coverage for the ${m.name} market — every figure sourced.`; } export async function generateMetadata({ params }: Props): Promise { const { slug } = await params; const r = await getMetro(slug); if (!r.ok) return r.status === 404 ? NOT_FOUND_METADATA : { title: "Market" }; return pageMetadata({ title: `Data Centers in ${r.data.name}`, description: describe(r.data), path: routes.market(r.data.slug) }); } const CONSTRAINT_LABEL: Record = { power: "Power", land: "Land", water: "Water", moratorium: "Moratorium", grid: "Grid" }; function Mw({ mw, estimate }: { mw: number | null | undefined; estimate?: boolean }) { const s = splitMw(mw); if (!s) return —; return ( <> {estimate && } {s.value} ); } const unitOf = (mw: number | null | undefined) => splitMw(mw)?.unit ?? undefined; export default async function MarketPage({ params, searchParams }: Props) { const [{ slug }, sp] = await Promise.all([params, searchParams]); const fPage = parseFPage(sp.fPage); const [r, { map }] = await Promise.all([getMetro(slug, fPage), countrySlugMap()]); if (!r.ok && r.status === 404) notFound(); if (!r.ok) { return ( ); } const m = r.data; const meta = (r.meta ?? {}) as DetailFacilitiesMeta; const facTotal = meta.facilitiesTotal ?? m.facilityCount; const facPage = meta.facilitiesPage ?? fPage; const facPer = meta.facilitiesPerPage ?? 50; const pipe = pipelineMw(m); const cHref = countryHref(map, m.countryIso2); const base = routes.market(m.slug); const aiFacilities = m.aiFacilities ?? []; const gridConstraints = m.gridConstraints ?? []; const claims = m.claims ?? []; const openingTimeline = m.openingTimeline ?? []; const aiCount = m.aiCount ?? aiFacilities.length; const statusData: Partial> = {}; if (m.operationalCount) statusData.operational = m.operationalCount; if (m.constructionCount) statusData.under_construction = m.constructionCount; if (m.plannedCount) statusData.announced = m.plannedCount; const other = m.facilityCount - m.operationalCount - m.constructionCount - m.plannedCount; if (other > 0) statusData.unknown = other; const hasCloud = m.cloudProviders.length > 0 || m.cloudRegions.length > 0; const powerItems = gridConstraints.length + m.constraints.length; const sections = [ { id: "map", label: "Map" }, { id: "status", label: "Status" }, { id: "operators", label: "Operators", count: m.operators.length }, { id: "pipeline", label: "Pipeline" }, { id: "openings", label: "Openings" }, { id: "concentration", label: "Concentration" }, { id: "momentum", label: "Momentum" }, { id: "connectivity", label: "Connectivity" }, { id: "power", label: "Power" }, { id: "coverage", label: "Coverage" }, { id: "ai", label: "AI", count: aiFacilities.length }, { id: "projects", label: "Projects", count: m.projects.length }, { id: "changes", label: "Changes", count: m.recentEvents.length }, { id: "facilities", label: "Facilities", count: facTotal }, { id: "claims", label: "Claims", count: claims.length }, ]; const jsonLd = compactJsonLd({ "@context": "https://schema.org", "@type": "Place", name: `${m.name} data center market`, url: `${routes.market(m.slug)}`, address: { "@type": "PostalAddress", addressLocality: m.name, addressRegion: m.regionName ?? undefined, addressCountry: m.countryIso2 }, geo: { "@type": "GeoCoordinates", latitude: m.lat, longitude: m.lng }, description: describe(m), }); const topOps = [...m.operators].sort((a, b) => b.facilityCount - a.facilityCount).slice(0, 15); const opsTotalFac = m.facilityCount || 1; return (
Market

Data centers in {m.name}

{cHref ? ( {m.countryName ?? m.countryIso2} ) : ( {m.countryName ?? m.countryIso2} )} {m.regionName && m.regionName !== m.name && ( <> · {m.regionName} )} · {m.lat.toFixed(2)}, {m.lng.toFixed(2)}
Explore →
{m.description &&

{m.description}

} {m.aliases.length > 0 && (
Sub-markets
{m.aliases.slice(0, 8).map((a) => ( {a} ))} {m.aliases.length > 8 && (
+{m.aliases.length - 8} more {m.aliases.slice(8).map((a) => ( {a} ))}
)}
)}
} unit={unitOf(m.knownMw)} label="Known MW" note={} size="sm" /> } unit={unitOf(pipe)} label="Pipeline MW" note={pipe === null ? `${fmtInt(m.constructionCount + m.plannedCount)} ${pluralize(m.constructionCount + m.plannedCount, "site")}, MW not published` : `${fmtInt(m.constructionCount)} under construction · ${fmtInt(m.plannedCount)} planned`} size="sm" /> {m.rankings.length > 0 && (
{m.rankings.map((rk) => ( #{rk.rank} / {rk.total} {rk.label} ))}
)}
{fmtInt(m.operatorCount)} total}> {topOps.length ? ({ key: o.id, label: o.name, href: routes.operator(o.slug), count: o.facilityCount, share: o.facilityCount / opsTotalFac, mw: o.knownMw ?? null }))} unit="facilities" /> : }
All projects →}> {m.pipeline ? : }
{openingTimeline.length ? ( ) : ( )} {m.growth.length >= 2 && (
Cumulative growth
)}
{m.concentration ? : }
{m.momentum ? : }
Connectivity index →}> {hasCloud ? (
Cloud
{m.cloudProviders.length > 0 && (
    {m.cloudProviders.map((p) => (
  • {p.name} {p.regionCount} {p.regionCount === 1 ? "region" : "regions"}
  • ))}
)} {m.cloudRegions.length > 0 && ( {m.cloudRegions.map((cr) => ( ))}
Region Provider Code AZs Status Launched
{cr.name}
{cr.provider.name} · {cr.code}
{cr.provider.name} {cr.code} {cr.availabilityZones ?? —} {cr.status} {cr.isSovereign && ( Sovereign )} {fmtDate(cr.launchedOn)}
)}

Cloud regions are associated with the market, not hosted at a specific facility unless a source says so.

) : ( )}
Internet exchanges
{m.ixps.length ? (
    {m.ixps.map((x) => (
  • {x.name} {x.networkCount === null ? "networks —" : `${fmtInt(x.networkCount)} networks`}
  • ))}
) : ( )}
Power & grid →}> {m.constraints.length > 0 && (
Constraint mentions in indexed sources
    {m.constraints.map((c, i) => (
  • {CONSTRAINT_LABEL[c.kind]} {c.date && ( )}

    {c.summary}

    {c.sourceName}
  • ))}
)}
Coverage report →}> {m.coverage ? : }
{aiFacilities.length > 0 && (
AI infrastructure →}>
)} {m.projects.length > 0 && (
All projects →}>
)} {m.recentEvents.length > 0 && (
Live feed →}>
)}
{fmtInt(facTotal)}}> fPageHref(base, sp, p)} columns={["operator", "status", "mw", "type", "confidence"]} emptyTitle="No facilities indexed in this market yet" />
{claims.length > 0 && (
)}
); }